﻿using System;
using System.Collections.Generic;
using System.Linq;
using PicoContainer.Defaults;

namespace XEvolve.Core.Runtime.Internal
{
    internal class PicoBasedExtensionRegistry : IExtensionRegistry, IServiceLocator
    {
        private bool started;
        private readonly DefaultPicoContainer context;
        private readonly IDictionary<object, PicoBasedExtensionPoint> extensionPoints;

        public PicoBasedExtensionRegistry()
        {
            context = new DefaultPicoContainer(new XComponentAdapterFactory(this, true));
            extensionPoints = new Dictionary<object, PicoBasedExtensionPoint>();
        }

        private PicoBasedExtensionPoint GetExtensionPointInternal(object extensionPointIdentifier)
        {
            if (!extensionPoints.ContainsKey(extensionPointIdentifier))
            {
                throw new ExtensionPointNotFoundException(extensionPointIdentifier);
            }
            return extensionPoints[extensionPointIdentifier];
        }

        public IExtensionPoint GetExtensionPoint(object identifier)
        {
            if (!extensionPoints.ContainsKey(identifier))
            {
                throw new ExtensionPointNotFoundException(identifier);
            }
            return extensionPoints[identifier];
        }

        public void RegisterExtensionPoint(object identifier)
        {
            RegisterExtensionPoint(identifier, true);
        }

        public void RegisterExtensionPoint(object identifier, bool isExtensionSingleton)
        {
            CheckStatus();
            if (extensionPoints.ContainsKey(identifier)) return;
            var extesnionPointContext = new DefaultPicoContainer(new XComponentAdapterFactory(this, isExtensionSingleton)) { Parent = context };
            extensionPoints[identifier] = new PicoBasedExtensionPoint(extesnionPointContext);
        }

        private void CheckStatus()
        {
            if (started)
            {
                throw new IllegalStateException();
            }
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionIdentifier, object extensionInstance)
        {
            CheckStatus();
            GetExtensionPointInternal(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionInstance);
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionIdentifier, Type extensionType)
        {
            CheckStatus();
            GetExtensionPointInternal(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionType);
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionInstance)
        {
            RegisterExtension(extensionPointIdentifier, Guid.NewGuid(), extensionInstance);
        }

        public void RegisterExtension(object extensionPointIdentifier, Type extensionType)
        {
            RegisterExtension(extensionPointIdentifier, Guid.NewGuid(), extensionType);
        }

        public void Start()
        {
            if (started) return;
            context.Start();
            started = true;
        }

        public void Stop()
        {
            if (!started) return;
            context.Stop();
            started = false;
        }

        internal void RegisterService(object identifier, Type implementation)
        {
            CheckStatus();
            context.RegisterComponentImplementation(identifier, implementation);
        }

        internal void RegisterService(object identifier, object instance)
        {
            CheckStatus();
            context.RegisterComponentInstance(identifier, instance);
        }


        public object GetService(Type api)
        {
            return context.GetComponentInstance(api);
        }

        public Api GetService<Api>()
        {
            return (Api) GetService(typeof (Api));
        }

        public object[] GetServices(Type api)
        {
            return context.GetComponentInstancesOfType(api).Cast<object>().ToArray();
        }

        public Api[] GetServices<Api>()
        {
            return context.GetComponentInstancesOfType(typeof(Api)).Cast<Api>().ToArray();
        }

    }
}
